﻿using System;
using System.Collections.Generic;
using MathExpression;
using NUnit.Framework;
using SimplexSolver;

namespace LPTask.Test
{
    [TestFixture]
    public class SimplexTableFixture
    {
        [Test]
        public void Recalculate()
        {
            SimplexTable table = CreateTable();
            table.Recalculate(3, 1);

            Assert.AreEqual(2.0/3.0, (table.TargetValue.Data as ScalarData).Value, GlobalVariables.EPSILON);
            IMathExpression[] vector = table.GetSolutionVector(3);
            Assert.IsNotNull(vector);
            Assert.AreEqual(2.0/9.0, (vector[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(0.0, (vector[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(0.0, (vector[2].Data as ScalarData).Value, GlobalVariables.EPSILON);

            IMathExpression[] target = table.TargetRow;
            Assert.AreEqual(1.0 / 3.0, (target[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(4.0, (target[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-10.0 / 3.0, (target[2].Data as ScalarData).Value, GlobalVariables.EPSILON);

            IMathExpression[] freeValues = table.FreeValueColumn;
            Assert.AreEqual(34.0/9.0, (freeValues[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(62.0/9.0, (freeValues[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(2.0/9.0, (freeValues[2].Data as ScalarData).Value, GlobalVariables.EPSILON);

            IMathExpression[][] matrix = table.RestrictionMatrix;
            Assert.AreEqual(-1.0/9.0, (matrix[0][0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-2.0, (matrix[0][1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(28.0/9.0, (matrix[0][2].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-5.0/9.0, (matrix[1][0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-6.0, (matrix[1][1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(68.0/9.0, (matrix[1][2].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(1.0/9.0, (matrix[2][0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(0.0, (matrix[2][1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-1.0/9.0, (matrix[2][2].Data as ScalarData).Value, GlobalVariables.EPSILON);

            Assert.AreEqual("L", table.RowHeaders[0]);
            Assert.AreEqual("y0", table.RowHeaders[1]);
            Assert.AreEqual("y1", table.RowHeaders[2]);
            Assert.AreEqual("x0", table.RowHeaders[3]);
            Assert.AreEqual("b", table.ColumnHeaders[0]);
            Assert.AreEqual("y2", table.ColumnHeaders[1]);
            Assert.AreEqual("x1", table.ColumnHeaders[2]);
            Assert.AreEqual("x2", table.ColumnHeaders[3]);
        }

        [Test]
        public void RecalculateNegativeItem()
        {
            SimplexTable table = CreateTable();
            Assert.Throws<ArgumentException>(() => table.Recalculate(1, 2));
        }

        [Test]
        public void RecalculateParamTarget()
        {
            SimplexTable table = CreateParamTable();
            table.Recalculate(3, 1);

            var targetValueData = table.TargetValue.Data as PolynomialData;
            Assert.AreEqual(2.0/3.0, (targetValueData.Values[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-2.0 / 9.0, (targetValueData.Values[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            IMathExpression[] vector = table.GetSolutionVector(3);
            Assert.IsNotNull(vector);
            Assert.AreEqual(2.0/9.0, (vector[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(0.0, (vector[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(0.0, (vector[2].Data as ScalarData).Value, GlobalVariables.EPSILON);

            IMathExpression[] target = table.TargetRow;
            var targetItem = target[0].Data as PolynomialData;
            Assert.AreEqual(1.0 / 3.0, (targetItem.Values[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-1.0 / 9.0, (targetItem.Values[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            targetItem = target[1].Data as PolynomialData;
            Assert.AreEqual(4.0, (targetItem.Values[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(1.0, (targetItem.Values[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            targetItem = target[2].Data as PolynomialData;
            Assert.AreEqual(-10.0 / 3.0, (targetItem.Values[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(10.0 / 9.0, (targetItem.Values[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            
            IMathExpression[] freeValues = table.FreeValueColumn;
            Assert.AreEqual(34.0/9.0, (freeValues[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(62.0/9.0, (freeValues[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(2.0/9.0, (freeValues[2].Data as ScalarData).Value, GlobalVariables.EPSILON);

            IMathExpression[][] matrix = table.RestrictionMatrix;
            Assert.AreEqual(-1.0/9.0, (matrix[0][0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-2.0, (matrix[0][1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(28.0/9.0, (matrix[0][2].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-5.0/9.0, (matrix[1][0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-6.0, (matrix[1][1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(68.0/9.0, (matrix[1][2].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(1.0/9.0, (matrix[2][0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(0.0, (matrix[2][1].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(-1.0/9.0, (matrix[2][2].Data as ScalarData).Value, GlobalVariables.EPSILON);

            Assert.AreEqual("L", table.RowHeaders[0]);
            Assert.AreEqual("y0", table.RowHeaders[1]);
            Assert.AreEqual("y1", table.RowHeaders[2]);
            Assert.AreEqual("x0", table.RowHeaders[3]);
            Assert.AreEqual("b", table.ColumnHeaders[0]);
            Assert.AreEqual("y2", table.ColumnHeaders[1]);
            Assert.AreEqual("x1", table.ColumnHeaders[2]);
            Assert.AreEqual("x2", table.ColumnHeaders[3]);
        }

        private SimplexTable CreateTable()
        {
            IList<IMathExpression> target = CreateTarget();
            IList<IMathExpression> freeValues = CreateFreeValues();
            IList<IMathExpression[]> matrix = CreateRestrictionMatrix();
            var table = new SimplexTable(target, freeValues, matrix);
            table.RowHeaders[1] = "y0";
            table.RowHeaders[2] = "y1";
            table.RowHeaders[3] = "y2";
            table.ColumnHeaders[1] = "x0";
            table.ColumnHeaders[2] = "x1";
            table.ColumnHeaders[3] = "x2";
            return table;
        }

        private SimplexTable CreateParamTable()
        {
            IList<IMathExpression> target = CreateParamTarget();
            IList<IMathExpression> freeValues = CreateFreeValues();
            IList<IMathExpression[]> matrix = CreateRestrictionMatrix();
            var table = new SimplexTable(target, freeValues, matrix);
            table.RowHeaders[1] = "y0";
            table.RowHeaders[2] = "y1";
            table.RowHeaders[3] = "y2";
            table.ColumnHeaders[1] = "x0";
            table.ColumnHeaders[2] = "x1";
            table.ColumnHeaders[3] = "x2";
            return table;
        }

        private IList<IMathExpression> CreateTarget()
        {
            var target = new List<IMathExpression>
                             {
                                 ExpressionFactory.Create(-3.0),
                                 ExpressionFactory.Create(4.0),
                                 ExpressionFactory.Create(-3.0)
                             };
            return target;
        }

        private IList<IMathExpression> CreateFreeValues()
        {
            var freeValues = new List<IMathExpression>
                                 {
                                     ExpressionFactory.Create(4.0),
                                     ExpressionFactory.Create(8.0),
                                     ExpressionFactory.Create(2.0)
                                 };
            return freeValues;
        }

        private IList<IMathExpression[]> CreateRestrictionMatrix()
        {
            var matrix = new List<IMathExpression[]>
                             {
                                 new[]
                                     {
                                         ExpressionFactory.Create(1.0),
                                         ExpressionFactory.Create(-2.0),
                                         ExpressionFactory.Create(3.0)
                                     },
                                 new[]
                                     {
                                         ExpressionFactory.Create(5.0),
                                         ExpressionFactory.Create(-6.0),
                                         ExpressionFactory.Create(7.0)
                                     },
                                 new[]
                                     {
                                         ExpressionFactory.Create(9.0),
                                         ExpressionFactory.Create(0.0),
                                         ExpressionFactory.Create(-1.0)
                                     }
                             };
            return matrix;
        }

        private IList<IMathExpression> CreateParamTarget()
        {
            var target = new List<IMathExpression>
                             {
                                 ExpressionFactory.Create(-3.0, 1.0, "t"),
                                 ExpressionFactory.Create(4.0, 1.0, "t"),
                                 ExpressionFactory.Create(-3.0, 1.0, "t")
                             };
            return target;
        }
    }
}